package com.neoCalf.neoUser.webSocket;


import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.neoCalf.neoCommon.utils.DataTool;
import com.neoCalf.neoUser.pro.SocketPro;
import com.neoCalf.neoUser.webSocket.handler.HandlerHeartBeat;
import com.neoCalf.neoUser.webSocket.handler.HandlerWebSocket;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.net.InetAddress;
import java.util.concurrent.TimeUnit;


@Component
@Slf4j
public class NettyWebSocketStarter implements Runnable {
    private static final NioEventLoopGroup GROUP = new NioEventLoopGroup();
    private static final NioEventLoopGroup WORKER = new NioEventLoopGroup(2);
    @Resource
    HandlerWebSocket handlerWebSocket;
    @Resource
    HandlerHeartBeat handlerHeartBeat;
    @Resource
    SocketPro socketPro;
    @Autowired
    private NamingService namingService;

    @PreDestroy
    private static void close() {
        GROUP.shutdownGracefully();
        WORKER.shutdownGracefully();
    }

    private void nettyStart() {
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap()
                    .group(GROUP, WORKER)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<NioSocketChannel>() {
                        @Override
                        protected void initChannel(NioSocketChannel channel) throws Exception {
                            ChannelPipeline pipeline = channel.pipeline();
                            // 设置处理器
                            /*
                            对http协议的支持 使用http 的解码器 编码器
                             */
                            pipeline.addLast(new HttpServerCodec());
                            /*
                            聚合解码 httpRequest/httpContent/lastHttpContent到fullHttpRequest
                            保证接收的http请求的完整性
                             */
                            pipeline.addLast(new HttpObjectAggregator(64 * 1024));
                            /*
                            心跳
                            long readerIdleTime, 读超时时间
                            long writerIdleTime, 写超时时间
                            long allIdleTime, 所有类型超时时间
                            TimeUnit unit 单位
                             */
                            pipeline.addLast(new IdleStateHandler(socketPro.getHeartBeatTime(), 0, 0, TimeUnit.SECONDS));
                            pipeline.addLast(handlerHeartBeat);
                            // 将http升级为ws协议,对webSocket的支持
                            pipeline.addLast(new WebSocketServerProtocolHandler(socketPro.getContextPath(), null, true, 64 * 1024, true, true, 10000L));
                            pipeline.addLast(handlerWebSocket);
                        }
                    });
            Integer wsPort = socketPro.getPort();
            String property = System.getProperty("ws.port");
            if (DataTool.isStrLegal(property)) {
                wsPort = Integer.valueOf(property);
            }

            ChannelFuture ch = serverBootstrap.bind(wsPort).sync();
            Instance instance = new Instance();
            instance.setIp(socketPro.getIp());
            instance.setPort(wsPort);
            instance.setClusterName(socketPro.getClusterName());
            namingService.registerInstance(socketPro.getServiceName(), instance);
            log.info("【{}】----------netty启动成功----------", wsPort);
            ch.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            log.debug("netty启动失败");
        } catch (NacosException e) {
            log.debug("注册服务失败");
        } finally {
            close();
        }
    }

    @Override
    public void run() {
        nettyStart();
    }
}
